<!-- HTML header for doxygen 1.8.6-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<title>OpenCV: Face analytics pipeline with G-API</title>
<link href="../../opencv.ico" rel="shortcut icon" type="image/x-icon" />
<link href="../../tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="../../jquery.js"></script>
<script type="text/javascript" src="../../dynsections.js"></script>
<script type="text/javascript" src="../../tutorial-utils.js"></script>
<link href="../../search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="../../search/searchdata.js"></script>
<script type="text/javascript" src="../../search/search.js"></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js", "TeX/AMSmath.js", "TeX/AMSsymbols.js"],
    jax: ["input/TeX","output/HTML-CSS"],
});
//<![CDATA[
MathJax.Hub.Config(
{
  TeX: {
      Macros: {
          matTT: [ "\\[ \\left|\\begin{array}{ccc} #1 & #2 & #3\\\\ #4 & #5 & #6\\\\ #7 & #8 & #9 \\end{array}\\right| \\]", 9],
          fork: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ \\end{array} \\right.", 4],
          forkthree: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ #5 & \\mbox{#6}\\\\ \\end{array} \\right.", 6],
          forkfour: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ #5 & \\mbox{#6}\\\\ #7 & \\mbox{#8}\\\\ \\end{array} \\right.", 8],
          vecthree: ["\\begin{bmatrix} #1\\\\ #2\\\\ #3 \\end{bmatrix}", 3],
          vecthreethree: ["\\begin{bmatrix} #1 & #2 & #3\\\\ #4 & #5 & #6\\\\ #7 & #8 & #9 \\end{bmatrix}", 9],
          cameramatrix: ["#1 = \\begin{bmatrix} f_x & 0 & c_x\\\\ 0 & f_y & c_y\\\\ 0 & 0 & 1 \\end{bmatrix}", 1],
          distcoeffs: ["(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6 [, s_1, s_2, s_3, s_4[, \\tau_x, \\tau_y]]]]) \\text{ of 4, 5, 8, 12 or 14 elements}"],
          distcoeffsfisheye: ["(k_1, k_2, k_3, k_4)"],
          hdotsfor: ["\\dots", 1],
          mathbbm: ["\\mathbb{#1}", 1],
          bordermatrix: ["\\matrix{#1}", 1]
      }
  }
}
);
//]]>
</script><script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js"></script>
<link href="../../doxygen.css" rel="stylesheet" type="text/css" />
<link href="../../stylesheet.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<!--#include virtual="/google-search.html"-->
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  <td id="projectlogo"><img alt="Logo" src="../../opencv-logo-small.png"/></td>
  <td style="padding-left: 0.5em;">
   <div id="projectname">OpenCV
   &#160;<span id="projectnumber">4.5.2</span>
   </div>
   <div id="projectbrief">Open Source Computer Vision</div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "../../search",false,'Search');
</script>
<script type="text/javascript" src="../../menudata.js"></script>
<script type="text/javascript" src="../../menu.js"></script>
<script type="text/javascript">
$(function() {
  initMenu('../../',true,false,'search.php','Search');
  $(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

<div id="nav-path" class="navpath">
  <ul>
<li class="navelem"><a class="el" href="../../d9/df8/tutorial_root.html">OpenCV Tutorials</a></li><li class="navelem"><a class="el" href="../../df/d7e/tutorial_table_of_content_gapi.html">Graph API (gapi module)</a></li>  </ul>
</div>
</div><!-- top -->
<div class="header">
  <div class="headertitle">
<div class="title">Face analytics pipeline with G-API </div>  </div>
</div><!--header-->
<div class="contents">
<div class="toc"><h3>Table of Contents</h3>
<ul><li class="level1"><a href="#gapi_ifd_intro">Overview</a></li>
<li class="level1"><a href="#gapi_ifd_prereq">Prerequisites</a></li>
<li class="level1"><a href="#gapi_ifd_why">Introduction: why G-API</a></li>
<li class="level1"><a href="#gapi_ifd_overview">Pipeline overview</a></li>
<li class="level1"><a href="#gapi_ifd_constructing">Constructing a pipeline</a><ul><li class="level2"><a href="#gapi_ifd_declaring_nets">Declaring Deep Learning topologies</a></li>
<li class="level2"><a href="#gapi_ifd_gcomputation">Building a GComputation</a></li>
</ul>
</li>
<li class="level1"><a href="#gapi_ifd_configuration">Configuring the pipeline</a></li>
<li class="level1"><a href="#gapi_ifd_running">Running the pipeline</a></li>
<li class="level1"><a href="#gapi_ifd_comparison">Comparison with serial mode</a></li>
<li class="level1"><a href="#gapi_ifd_conclusion">Conclusion</a></li>
<li class="level1"><a href="#gapi_ifd_pp">Listing: post-processing kernel</a></li>
</ul>
</div>
<div class="textblock"><p><b>Next Tutorial:</b> <a class="el" href="../../d3/d7a/tutorial_gapi_anisotropic_segmentation.html">Porting anisotropic image segmentation on G-API</a></p>
<h1><a class="anchor" id="gapi_ifd_intro"></a>
Overview</h1>
<p>In this tutorial you will learn:</p><ul>
<li>How to integrate Deep Learning inference in a G-API graph;</li>
<li>How to run a G-API graph on a video stream and obtain data from it.</li>
</ul>
<h1><a class="anchor" id="gapi_ifd_prereq"></a>
Prerequisites</h1>
<p>This sample requires:</p><ul>
<li>PC with GNU/Linux or Microsoft Windows (Apple macOS is supported but was not tested);</li>
<li>OpenCV 4.2 or later built with Intel® Distribution of <a href="https://docs.openvinotoolkit.org/">OpenVINO™ Toolkit</a> (building with <a href="https://www.threadingbuildingblocks.org/intel-tbb-tutorial">Intel® TBB</a> is a plus);</li>
<li>The following topologies from OpenVINO™ Toolkit <a href="https://github.com/opencv/open_model_zoo">Open Model Zoo</a>:<ul>
<li><code>face-detection-adas-0001</code>;</li>
<li><code>age-gender-recognition-retail-0013</code>;</li>
<li><code>emotions-recognition-retail-0003</code>.</li>
</ul>
</li>
</ul>
<h1><a class="anchor" id="gapi_ifd_why"></a>
Introduction: why G-API</h1>
<p>Many computer vision algorithms run on a video stream rather than on individual images. Stream processing usually consists of multiple steps &ndash; like decode, preprocessing, detection, tracking, classification (on detected objects), and visualization &ndash; forming a <em>video processing pipeline</em>. Moreover, many these steps of such pipeline can run in parallel &ndash; modern platforms have different hardware blocks on the same chip like decoders and GPUs, and extra accelerators can be plugged in as extensions, like Intel® Movidius™ Neural Compute Stick for deep learning offload.</p>
<p>Given all this manifold of options and a variety in video analytics algorithms, managing such pipelines effectively quickly becomes a problem. For sure it can be done manually, but this approach doesn't scale: if a change is required in the algorithm (e.g. a new pipeline step is added), or if it is ported on a new platform with different capabilities, the whole pipeline needs to be re-optimized.</p>
<p>Starting with version 4.2, OpenCV offers a solution to this problem. OpenCV G-API now can manage Deep Learning inference (a cornerstone of any modern analytics pipeline) with a traditional Computer Vision as well as video capturing/decoding, all in a single pipeline. G-API takes care of pipelining itself &ndash; so if the algorithm or platform changes, the execution model adapts to it automatically.</p>
<h1><a class="anchor" id="gapi_ifd_overview"></a>
Pipeline overview</h1>
<p>Our sample application is based on <a href="https://github.com/opencv/open_model_zoo/tree/master/demos/interactive_face_detection_demo">"Interactive Face Detection"</a> demo from OpenVINO™ Toolkit Open Model Zoo. A simplified pipeline consists of the following steps:</p><ol type="1">
<li>Image acquisition and decode;</li>
<li>Detection with preprocessing;</li>
<li>Classification with preprocessing for every detected object with two networks;</li>
<li>Visualization.</li>
</ol>
<div class="dotgraph">
<iframe scrolling="no" frameborder="0" src="../../dot_inline_dotgraph_3.svg" width="871" height="274"><p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p></iframe></div>
<h1><a class="anchor" id="gapi_ifd_constructing"></a>
Constructing a pipeline</h1>
<p>Constructing a G-API graph for a video streaming case does not differ much from a <a class="el" href="../../d0/d1e/gapi.html#gapi_example">regular usage</a> of G-API &ndash; it is still about defining graph <em>data</em> (with <a class="el" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>, <a class="el" href="../../d9/d98/classcv_1_1GScalar.html">cv::GScalar</a>, and <a class="el" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray</a>) and <em>operations</em> over it. Inference also becomes an operation in the graph, but is defined in a little bit different way.</p>
<h2><a class="anchor" id="gapi_ifd_declaring_nets"></a>
Declaring Deep Learning topologies</h2>
<p>In contrast with traditional CV functions (see <a class="el" href="../../df/d1f/group__gapi__core.html">core</a> and <a class="el" href="../../d2/d00/group__gapi__imgproc.html">imgproc</a>) where G-API declares distinct operations for every function, inference in G-API is a single generic operation <a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer&lt;&gt;</a>. As usual, it is just an interface and it can be implemented in a number of ways under the hood. In OpenCV 4.2, only OpenVINO™ Inference Engine-based backend is available, and OpenCV's own DNN module-based backend is to come.</p>
<p><a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer&lt;&gt;</a> is <em>parametrized</em> by the details of a topology we are going to execute. Like operations, topologies in G-API are strongly typed and are defined with a special macro <a class="el" href="../../d6/d32/infer_8hpp.html#adfb450a1d7992bc72c9afaa758516f27">G_API_NET()</a>:</p>
<div class="fragment"><div class="line"><span class="comment">// Face detector: takes one Mat, returns another Mat</span></div><div class="line"><a class="code" href="../../d6/d32/infer_8hpp.html#adfb450a1d7992bc72c9afaa758516f27">G_API_NET</a>(Faces, &lt;<a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>(<a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>)&gt;, <span class="stringliteral">&quot;face-detector&quot;</span>);</div><div class="line"></div><div class="line"><span class="comment">// Age/Gender recognition - takes one Mat, returns two:</span></div><div class="line"><span class="comment">// one for Age and one for Gender. In G-API, multiple-return-value operations</span></div><div class="line"><span class="comment">// are defined using std::tuple&lt;&gt;.</span></div><div class="line"><span class="keyword">using</span> AGInfo = std::tuple&lt;cv::GMat, cv::GMat&gt;;</div><div class="line"><a class="code" href="../../d6/d32/infer_8hpp.html#adfb450a1d7992bc72c9afaa758516f27">G_API_NET</a>(AgeGender, &lt;AGInfo(<a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>)&gt;,   <span class="stringliteral">&quot;age-gender-recoginition&quot;</span>);</div><div class="line"></div><div class="line"><span class="comment">// Emotion recognition - takes one Mat, returns another.</span></div><div class="line"><a class="code" href="../../d6/d32/infer_8hpp.html#adfb450a1d7992bc72c9afaa758516f27">G_API_NET</a>(Emotions, &lt;<a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>(<a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>)&gt;, <span class="stringliteral">&quot;emotions-recognition&quot;</span>);</div></div><!-- fragment --><p> Similar to how operations are defined with <a class="el" href="../../de/d7f/gkernel_8hpp.html#a75c975472536f4243e95150c51e8a3be">G_API_OP()</a>, network description requires three parameters:</p><ol type="1">
<li>A type name. Every defined topology is declared as a distinct C++ type which is used further in the program &ndash; see below;</li>
<li>A <code>std::function&lt;&gt;</code>-like API signature. G-API traits networks as regular "functions" which take and return data. Here network <code>Faces</code> (a detector) takes a <a class="el" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a> and returns a <a class="el" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>, while network <code>AgeGender</code> is known to provide two outputs (age and gender blobs, respectively) &ndash; so its has a <code>std::tuple&lt;&gt;</code> as a return type.</li>
<li>A topology name &ndash; can be any non-empty string, G-API is using these names to distinguish networks inside. Names should be unique in the scope of a single graph.</li>
</ol>
<h2><a class="anchor" id="gapi_ifd_gcomputation"></a>
Building a GComputation</h2>
<p>Now the above pipeline is expressed in G-API like this:</p>
<div class="fragment"><div class="line">    <a class="code" href="../../d9/dfe/classcv_1_1GComputation.html">cv::GComputation</a> pp([]() {</div><div class="line">            <span class="comment">// Declare an empty GMat - the beginning of the pipeline.</span></div><div class="line">            <a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a> in;</div><div class="line"></div><div class="line">            <span class="comment">// Run face detection on the input frame. Result is a single GMat,</span></div><div class="line">            <span class="comment">// internally representing an 1x1x200x7 SSD output.</span></div><div class="line">            <span class="comment">// This is a single-patch version of infer:</span></div><div class="line">            <span class="comment">// - Inference is running on the whole input image;</span></div><div class="line">            <span class="comment">// - Image is converted and resized to the network&#39;s expected format</span></div><div class="line">            <span class="comment">//   automatically.</span></div><div class="line">            <a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a> detections = cv::gapi::infer&lt;custom::Faces&gt;(in);</div><div class="line"></div><div class="line">            <span class="comment">// Parse SSD output to a list of ROI (rectangles) using</span></div><div class="line">            <span class="comment">// a custom kernel. Note: parsing SSD may become a &quot;standard&quot; kernel.</span></div><div class="line">            <a class="code" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray&lt;cv::Rect&gt;</a> faces = custom::PostProc::on(detections, in);</div><div class="line"></div><div class="line">            <span class="comment">// Now run Age/Gender model on every detected face. This model has two</span></div><div class="line">            <span class="comment">// outputs (for age and gender respectively).</span></div><div class="line">            <span class="comment">// A special ROI-list-oriented form of infer&lt;&gt;() is used here:</span></div><div class="line">            <span class="comment">// - First input argument is the list of rectangles to process,</span></div><div class="line">            <span class="comment">// - Second one is the image where to take ROI from;</span></div><div class="line">            <span class="comment">// - Crop/Resize/Layout conversion happens automatically for every image patch</span></div><div class="line">            <span class="comment">//   from the list</span></div><div class="line">            <span class="comment">// - Inference results are also returned in form of list (GArray&lt;&gt;)</span></div><div class="line">            <span class="comment">// - Since there&#39;re two outputs, infer&lt;&gt; return two arrays (via std::tuple).</span></div><div class="line">            <a class="code" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray&lt;cv::GMat&gt;</a> ages;</div><div class="line">            <a class="code" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray&lt;cv::GMat&gt;</a> genders;</div><div class="line">            std::tie(ages, genders) = cv::gapi::infer&lt;custom::AgeGender&gt;(faces, in);</div><div class="line"></div><div class="line">            <span class="comment">// Recognize emotions on every face.</span></div><div class="line">            <span class="comment">// ROI-list-oriented infer&lt;&gt;() is used here as well.</span></div><div class="line">            <span class="comment">// Since custom::Emotions network produce a single output, only one</span></div><div class="line">            <span class="comment">// GArray&lt;&gt; is returned here.</span></div><div class="line">            <a class="code" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray&lt;cv::GMat&gt;</a> emotions = cv::gapi::infer&lt;custom::Emotions&gt;(faces, in);</div><div class="line"></div><div class="line">            <span class="comment">// Return the decoded frame as a result as well.</span></div><div class="line">            <span class="comment">// Input matrix can&#39;t be specified as output one, so use copy() here</span></div><div class="line">            <span class="comment">// (this copy will be optimized out in the future).</span></div><div class="line">            <a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a> frame = <a class="code" href="../../d6/d91/group__gapi__transform.html#gac782e501961826d93c5556e623fca3c3">cv::gapi::copy</a>(in);</div><div class="line"></div><div class="line">            <span class="comment">// Now specify the computation&#39;s boundaries - our pipeline consumes</span></div><div class="line">            <span class="comment">// one images and produces five outputs.</span></div><div class="line">            <span class="keywordflow">return</span> <a class="code" href="../../d9/dfe/classcv_1_1GComputation.html">cv::GComputation</a>(<a class="code" href="../../d2/d75/namespacecv.html#a8e40d34081b18c79ba4c3cfb9fd0634f">cv::GIn</a>(in),</div><div class="line">                                    <a class="code" href="../../d2/d75/namespacecv.html#adaa9a308669926cecc4793c6e4449629">cv::GOut</a>(frame, faces, ages, genders, emotions));</div><div class="line">        });</div></div><!-- fragment --><p> Every pipeline starts with declaring empty data objects &ndash; which act as inputs to the pipeline. Then we call a generic <a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer&lt;&gt;</a> specialized to <code>Faces</code> detection network. <a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer&lt;&gt;</a> inherits its signature from its template parameter &ndash; and in this case it expects one input <a class="el" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a> and produces one output <a class="el" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>.</p>
<p>In this sample we use a pre-trained SSD-based network and its output needs to be parsed to an array of detections (object regions of interest, ROIs). It is done by a custom operation <code>custom::PostProc</code>, which returns an array of rectangles (of type <code><a class="el" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray</a>&lt;<a class="el" href="../../dc/d84/group__core__basic.html#ga11d95de507098e90bad732b9345402e8">cv::Rect</a>&gt;</code>) back to the pipeline. This operation also filters out results by a confidence threshold &ndash; and these details are hidden in the kernel itself. Still, at the moment of graph construction we operate with interfaces only and don't need actual kernels to express the pipeline &ndash; so the implementation of this post-processing will be listed later.</p>
<p>After detection result output is parsed to an array of objects, we can run classification on any of those. G-API doesn't support syntax for in-graph loops like <code>for_each()</code> yet, but instead <a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer&lt;&gt;</a> comes with a special list-oriented overload.</p>
<p>User can call <a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer&lt;&gt;</a> with a <a class="el" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray</a> as the first argument, so then G-API assumes it needs to run the associated network on every rectangle from the given list of the given frame (second argument). Result of such operation is also a list &ndash; a <a class="el" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray</a> of <a class="el" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>.</p>
<p>Since <code>AgeGender</code> network itself produces two outputs, it's output type for a list-based version of <a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer</a> is a tuple of arrays. We use <code>std::tie()</code> to decompose this input into two distinct objects.</p>
<p><code>Emotions</code> network produces a single output so its list-based inference's return type is <code><a class="el" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray</a>&lt;<a class="el" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>&gt;</code>.</p>
<h1><a class="anchor" id="gapi_ifd_configuration"></a>
Configuring the pipeline</h1>
<p>G-API strictly separates construction from configuration &ndash; with the idea to keep algorithm code itself platform-neutral. In the above listings we only declared our operations and expressed the overall data flow, but didn't even mention that we use OpenVINO™. We only described <em>what</em> we do, but not <em>how</em> we do it. Keeping these two aspects clearly separated is the design goal for G-API.</p>
<p>Platform-specific details arise when the pipeline is <em>compiled</em> &ndash; i.e. is turned from a declarative to an executable form. The way <em>how</em> to run stuff is specified via compilation arguments, and new inference/streaming features are no exception from this rule.</p>
<p>G-API is built on backends which implement interfaces (see <a class="el" href="../../de/d4d/gapi_hld.html">Architecture</a> and <a class="el" href="../../d0/d25/gapi_kernel_api.html">Kernels</a> for details) &ndash; thus <a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer&lt;&gt;</a> is a function which can be implemented by different backends. In OpenCV 4.2, only OpenVINO™ Inference Engine backend for inference is available. Every inference backend in G-API has to provide a special parameterizable structure to express <em>backend-specific</em> neural network parameters &ndash; and in this case, it is <a class="el" href="../../d7/dda/classcv_1_1gapi_1_1ie_1_1Params.html">cv::gapi::ie::Params</a>:</p>
<div class="fragment"><div class="line">    <span class="keyword">auto</span> det_net = <a class="code" href="../../d7/dda/classcv_1_1gapi_1_1ie_1_1Params.html">cv::gapi::ie::Params&lt;custom::Faces&gt;</a> {</div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;fdm&quot;</span>),   <span class="comment">// read cmd args: path to topology IR</span></div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;fdw&quot;</span>),   <span class="comment">// read cmd args: path to weights</span></div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;fdd&quot;</span>),   <span class="comment">// read cmd args: device specifier</span></div><div class="line">    };</div><div class="line"></div><div class="line">    <span class="keyword">auto</span> age_net = <a class="code" href="../../d7/dda/classcv_1_1gapi_1_1ie_1_1Params.html">cv::gapi::ie::Params&lt;custom::AgeGender&gt;</a> {</div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;agem&quot;</span>),   <span class="comment">// read cmd args: path to topology IR</span></div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;agew&quot;</span>),   <span class="comment">// read cmd args: path to weights</span></div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;aged&quot;</span>),   <span class="comment">// read cmd args: device specifier</span></div><div class="line">    }.cfgOutputLayers({ <span class="stringliteral">&quot;age_conv3&quot;</span>, <span class="stringliteral">&quot;prob&quot;</span> });</div><div class="line"></div><div class="line">    <span class="keyword">auto</span> emo_net = <a class="code" href="../../d7/dda/classcv_1_1gapi_1_1ie_1_1Params.html">cv::gapi::ie::Params&lt;custom::Emotions&gt;</a> {</div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;emom&quot;</span>),   <span class="comment">// read cmd args: path to topology IR</span></div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;emow&quot;</span>),   <span class="comment">// read cmd args: path to weights</span></div><div class="line">        cmd.get&lt;std::string&gt;(<span class="stringliteral">&quot;emod&quot;</span>),   <span class="comment">// read cmd args: device specifier</span></div><div class="line">    };</div></div><!-- fragment --><p> Here we define three parameter objects: <code>det_net</code>, <code>age_net</code>, and <code>emo_net</code>. Every object is a <a class="el" href="../../d7/dda/classcv_1_1gapi_1_1ie_1_1Params.html">cv::gapi::ie::Params</a> structure parametrization for each particular network we use. On a compilation stage, G-API automatically matches network parameters with their <a class="el" href="../../d4/d1c/namespacecv_1_1gapi.html#a4ad741555c257e68e542becbf65b8185" title="Calculates response for the specified network (template parameter) for the specified region in the so...">cv::gapi::infer&lt;&gt;</a> calls in graph using this information.</p>
<p>Regardless of the topology, every parameter structure is constructed with three string arguments &ndash; specific to the OpenVINO™ Inference Engine:</p><ol type="1">
<li>Path to the topology's intermediate representation (.xml file);</li>
<li>Path to the topology's model weights (.bin file);</li>
<li>Device where to run &ndash; "CPU", "GPU", and others &ndash; based on your OpenVINO™ Toolkit installation. These arguments are taken from the command-line parser.</li>
</ol>
<p>Once networks are defined and custom kernels are implemented, the pipeline is compiled for streaming:</p>
<div class="fragment"><div class="line">    <span class="comment">// Form a kernel package (with a single OpenCV-based implementation of our</span></div><div class="line">    <span class="comment">// post-processing) and a network package (holding our three networks).</span></div><div class="line">    <span class="keyword">auto</span> kernels = cv::gapi::kernels&lt;custom::OCVPostProc&gt;();</div><div class="line">    <span class="keyword">auto</span> <a class="code" href="../../d4/d1c/namespacecv_1_1gapi.html#a7d2b842e9369c0d72b383e61b9038581">networks</a> = <a class="code" href="../../d4/d1c/namespacecv_1_1gapi.html#a7d2b842e9369c0d72b383e61b9038581">cv::gapi::networks</a>(det_net, age_net, emo_net);</div><div class="line"></div><div class="line">    <span class="comment">// Compile our pipeline and pass our kernels &amp; networks as</span></div><div class="line">    <span class="comment">// parameters.  This is the place where G-API learns which</span></div><div class="line">    <span class="comment">// networks &amp; kernels we&#39;re actually operating with (the graph</span></div><div class="line">    <span class="comment">// description itself known nothing about that).</span></div><div class="line">    <span class="keyword">auto</span> cc = pp.compileStreaming(<a class="code" href="../../d9/d29/group__gapi__compile__args.html#ga3ccf2a52953f18bb3e4c01243cc4e679">cv::compile_args</a>(kernels, networks));</div></div><!-- fragment --><p> <a class="el" href="../../d9/dfe/classcv_1_1GComputation.html#a53af72f7dafa8cbf416581628aaa32fd" title="Compile the computation for streaming mode. ">cv::GComputation::compileStreaming()</a> triggers a special video-oriented form of graph compilation where G-API is trying to optimize throughput. Result of this compilation is an object of special type <a class="el" href="../../d1/d9b/classcv_1_1GStreamingCompiled.html" title="Represents a computation (graph) compiled for streaming. ">cv::GStreamingCompiled</a> &ndash; in constract to a traditional callable <a class="el" href="../../d2/d2c/classcv_1_1GCompiled.html" title="Represents a compiled computation (graph). Can only be used with image / data formats &amp; resolutions i...">cv::GCompiled</a>, these objects are closer to media players in their semantics.</p>
<dl class="section note"><dt>Note</dt><dd>There is no need to pass metadata arguments describing the format of the input video stream in <a class="el" href="../../d9/dfe/classcv_1_1GComputation.html#a53af72f7dafa8cbf416581628aaa32fd" title="Compile the computation for streaming mode. ">cv::GComputation::compileStreaming()</a> &ndash; G-API figures automatically what are the formats of the input vector and adjusts the pipeline to these formats on-the-fly. User still can pass metadata there as with regular <a class="el" href="../../d9/dfe/classcv_1_1GComputation.html#ab11bcc0578f9aa5ec8f8eaf6cda0dfa8" title="Compile the computation for specific input format(s). ">cv::GComputation::compile()</a> in order to fix the pipeline to the specific input format.</dd></dl>
<h1><a class="anchor" id="gapi_ifd_running"></a>
Running the pipeline</h1>
<p>Pipelining optimization is based on processing multiple input video frames simultaneously, running different steps of the pipeline in parallel. This is why it works best when the framework takes full control over the video stream.</p>
<p>The idea behind streaming API is that user specifies an <em>input source</em> to the pipeline and then G-API manages its execution automatically until the source ends or user interrupts the execution. G-API pulls new image data from the source and passes it to the pipeline for processing.</p>
<p>Streaming sources are represented by the interface <a class="el" href="../../da/d03/classcv_1_1gapi_1_1wip_1_1IStreamSource.html" title="Abstract streaming pipeline source. ">cv::gapi::wip::IStreamSource</a>. Objects implementing this interface may be passed to <code>GStreamingCompiled</code> as regular inputs via <code><a class="el" href="../../d2/d75/namespacecv.html#a4941eb8cbe70b8c7187bc8459af7653b">cv::gin()</a></code> helper function. In OpenCV 4.2, only one streaming source is allowed per pipeline &ndash; this requirement will be relaxed in the future.</p>
<p>OpenCV comes with a great class <a class="el" href="../../d8/dfe/classcv_1_1VideoCapture.html" title="Class for video capturing from video files, image sequences or cameras. ">cv::VideoCapture</a> and by default G-API ships with a stream source class based on it &ndash; <a class="el" href="../../dd/d1c/classcv_1_1gapi_1_1wip_1_1GCaptureSource.html" title="OpenCV&#39;s VideoCapture-based streaming source. ">cv::gapi::wip::GCaptureSource</a>. Users can implement their own streaming sources e.g. using <a href="https://01.org/vaapi">VAAPI</a> or other Media or Networking APIs.</p>
<p>Sample application specifies the input source as follows:</p>
<div class="fragment"><div class="line">        <span class="keyword">auto</span> in_src = cv::gapi::wip::make_src&lt;cv::gapi::wip::GCaptureSource&gt;(input);</div><div class="line">        cc.setSource(<a class="code" href="../../d2/d75/namespacecv.html#a4941eb8cbe70b8c7187bc8459af7653b">cv::gin</a>(in_src));</div></div><!-- fragment --><p> Please note that a GComputation may still have multiple inputs like <a class="el" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>, <a class="el" href="../../d9/d98/classcv_1_1GScalar.html">cv::GScalar</a>, or <a class="el" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray</a> objects. User can pass their respective host-side types (<a class="el" href="../../d3/d63/classcv_1_1Mat.html" title="n-dimensional dense array class ">cv::Mat</a>, <a class="el" href="../../dc/d84/group__core__basic.html#ga599fe92e910c027be274233eccad7beb">cv::Scalar</a>, std::vector&lt;&gt;) in the input vector as well, but in Streaming mode these objects will create "endless" constant streams. Mixing a real video source stream and a const data stream is allowed.</p>
<p>Running a pipeline is easy &ndash; just call <a class="el" href="../../d1/d9b/classcv_1_1GStreamingCompiled.html#a3cc45dcb57acab91359b4e8493bb39a4" title="Start the pipeline execution. ">cv::GStreamingCompiled::start()</a> and fetch your data with blocking cv::GStreamingCompiled::pull() or non-blocking <a class="el" href="../../d1/d9b/classcv_1_1GStreamingCompiled.html#af4a40c09634ba45686b2177f38c49022" title="Try to get the next processed frame from the pipeline. ">cv::GStreamingCompiled::try_pull()</a>; repeat until the stream ends:</p>
<div class="fragment"><div class="line">        <span class="comment">// After data source is specified, start the execution</span></div><div class="line">        cc.start();</div><div class="line"></div><div class="line">        <span class="comment">// Declare data objects we will be receiving from the pipeline.</span></div><div class="line">        <a class="code" href="../../d3/d63/classcv_1_1Mat.html">cv::Mat</a> frame;                      <span class="comment">// The captured frame itself</span></div><div class="line">        std::vector&lt;cv::Rect&gt; faces;        <span class="comment">// Array of detected faces</span></div><div class="line">        std::vector&lt;cv::Mat&gt; out_ages;      <span class="comment">// Array of inferred ages (one blob per face)</span></div><div class="line">        std::vector&lt;cv::Mat&gt; out_genders;   <span class="comment">// Array of inferred genders (one blob per face)</span></div><div class="line">        std::vector&lt;cv::Mat&gt; out_emotions;  <span class="comment">// Array of classified emotions (one blob per face)</span></div><div class="line"></div><div class="line">        <span class="comment">// Implement different execution policies depending on the display option</span></div><div class="line">        <span class="comment">// for the best performance.</span></div><div class="line">        <span class="keywordflow">while</span> (cc.running()) {</div><div class="line">            <span class="keyword">auto</span> out_vector = <a class="code" href="../../d2/d75/namespacecv.html#ac82b8a261b82157293b603b55c096a9e">cv::gout</a>(frame, faces, out_ages, out_genders, out_emotions);</div><div class="line">            <span class="keywordflow">if</span> (no_show) {</div><div class="line">                <span class="comment">// This is purely a video processing. No need to balance</span></div><div class="line">                <span class="comment">// with UI rendering.  Use a blocking pull() to obtain</span></div><div class="line">                <span class="comment">// data. Break the loop if the stream is over.</span></div><div class="line">                <span class="keywordflow">if</span> (!cc.pull(std::move(out_vector)))</div><div class="line">                    <span class="keywordflow">break</span>;</div><div class="line">            } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (!cc.try_pull(std::move(out_vector))) {</div><div class="line">                <span class="comment">// Use a non-blocking try_pull() to obtain data.</span></div><div class="line">                <span class="comment">// If there&#39;s no data, let UI refresh (and handle keypress)</span></div><div class="line">                <span class="keywordflow">if</span> (<a class="code" href="../../d7/dfc/group__highgui.html#ga5628525ad33f52eab17feebcfba38bd7">cv::waitKey</a>(1) &gt;= 0) <span class="keywordflow">break</span>;</div><div class="line">                <span class="keywordflow">else</span> <span class="keywordflow">continue</span>;</div><div class="line">            }</div><div class="line">            <span class="comment">// At this point we have data for sure (obtained in either</span></div><div class="line">            <span class="comment">// blocking or non-blocking way).</span></div><div class="line">            frames++;</div><div class="line">            labels::DrawResults(frame, faces, out_ages, out_genders, out_emotions);</div><div class="line">            labels::DrawFPS(frame, frames, avg.fps(frames));</div><div class="line">            <span class="keywordflow">if</span> (!no_show) <a class="code" href="../../d7/dfc/group__highgui.html#ga453d42fe4cb60e5723281a89973ee563">cv::imshow</a>(<span class="stringliteral">&quot;Out&quot;</span>, frame);</div><div class="line">        }</div></div><!-- fragment --><p> The above code may look complex but in fact it handles two modes &ndash; with and without graphical user interface (GUI):</p><ul>
<li>When a sample is running in a "headless" mode (<code>--pure</code> option is set), this code simply pulls data from the pipeline with the blocking <code>pull()</code> until it ends. This is the most performant mode of execution.</li>
<li>When results are also displayed on the screen, the Window System needs to take some time to refresh the window contents and handle GUI events. In this case, the demo pulls data with a non-blocking <code>try_pull()</code> until there is no more data available (but it does not mark end of the stream &ndash; just means new data is not ready yet), and only then displays the latest obtained result and refreshes the screen. Reducing the time spent in GUI with this trick increases the overall performance a little bit.</li>
</ul>
<h1><a class="anchor" id="gapi_ifd_comparison"></a>
Comparison with serial mode</h1>
<p>The sample can also run in a serial mode for a reference and benchmarking purposes. In this case, a regular <a class="el" href="../../d9/dfe/classcv_1_1GComputation.html#ab11bcc0578f9aa5ec8f8eaf6cda0dfa8" title="Compile the computation for specific input format(s). ">cv::GComputation::compile()</a> is used and a regular single-frame <a class="el" href="../../d2/d2c/classcv_1_1GCompiled.html" title="Represents a compiled computation (graph). Can only be used with image / data formats &amp; resolutions i...">cv::GCompiled</a> object is produced; the pipelining optimization is not applied within G-API; it is the user responsibility to acquire image frames from <a class="el" href="../../d8/dfe/classcv_1_1VideoCapture.html" title="Class for video capturing from video files, image sequences or cameras. ">cv::VideoCapture</a> object and pass those to G-API.</p>
<div class="fragment"><div class="line">        <a class="code" href="../../d8/dfe/classcv_1_1VideoCapture.html">cv::VideoCapture</a> cap(input);</div><div class="line">        <a class="code" href="../../d3/d63/classcv_1_1Mat.html">cv::Mat</a> in_frame, frame;            <span class="comment">// The captured frame itself</span></div><div class="line">        std::vector&lt;cv::Rect&gt; faces;        <span class="comment">// Array of detected faces</span></div><div class="line">        std::vector&lt;cv::Mat&gt; out_ages;      <span class="comment">// Array of inferred ages (one blob per face)</span></div><div class="line">        std::vector&lt;cv::Mat&gt; out_genders;   <span class="comment">// Array of inferred genders (one blob per face)</span></div><div class="line">        std::vector&lt;cv::Mat&gt; out_emotions;  <span class="comment">// Array of classified emotions (one blob per face)</span></div><div class="line"></div><div class="line">        <span class="keywordflow">while</span> (cap.read(in_frame)) {</div><div class="line">            pp.apply(<a class="code" href="../../d2/d75/namespacecv.html#a4941eb8cbe70b8c7187bc8459af7653b">cv::gin</a>(in_frame),</div><div class="line">                     <a class="code" href="../../d2/d75/namespacecv.html#ac82b8a261b82157293b603b55c096a9e">cv::gout</a>(frame, faces, out_ages, out_genders, out_emotions),</div><div class="line">                     <a class="code" href="../../d9/d29/group__gapi__compile__args.html#ga3ccf2a52953f18bb3e4c01243cc4e679">cv::compile_args</a>(kernels, networks));</div><div class="line">            labels::DrawResults(frame, faces, out_ages, out_genders, out_emotions);</div><div class="line">            frames++;</div><div class="line">            <span class="keywordflow">if</span> (frames == 1u) {</div><div class="line">                <span class="comment">// Start timer only after 1st frame processed -- compilation</span></div><div class="line">                <span class="comment">// happens on-the-fly here</span></div><div class="line">                avg.start();</div><div class="line">            } <span class="keywordflow">else</span> {</div><div class="line">                <span class="comment">// Measurfe &amp; draw FPS for all other frames</span></div><div class="line">                labels::DrawFPS(frame, frames, avg.fps(frames-1));</div><div class="line">            }</div><div class="line">            <span class="keywordflow">if</span> (!no_show) {</div><div class="line">                <a class="code" href="../../d7/dfc/group__highgui.html#ga453d42fe4cb60e5723281a89973ee563">cv::imshow</a>(<span class="stringliteral">&quot;Out&quot;</span>, frame);</div><div class="line">                <span class="keywordflow">if</span> (<a class="code" href="../../d7/dfc/group__highgui.html#ga5628525ad33f52eab17feebcfba38bd7">cv::waitKey</a>(1) &gt;= 0) <span class="keywordflow">break</span>;</div><div class="line">            }</div><div class="line">        }</div></div><!-- fragment --><p> On a test machine (Intel® Core™ i5-6600), with OpenCV built with [Intel® TBB] support, detector network assigned to CPU, and classifiers to iGPU, the pipelined sample outperformes the serial one by the factor of 1.36x (thus adding +36% in overall throughput).</p>
<h1><a class="anchor" id="gapi_ifd_conclusion"></a>
Conclusion</h1>
<p>G-API introduces a technological way to build and optimize hybrid pipelines. Switching to a new execution model does not require changes in the algorithm code expressed with G-API &ndash; only the way how graph is triggered differs.</p>
<h1><a class="anchor" id="gapi_ifd_pp"></a>
Listing: post-processing kernel</h1>
<p>G-API gives an easy way to plug custom code into the pipeline even if it is running in a streaming mode and processing tensor data. Inference results are represented by multi-dimensional <a class="el" href="../../d3/d63/classcv_1_1Mat.html" title="n-dimensional dense array class ">cv::Mat</a> objects so accessing those is as easy as with a regular DNN module.</p>
<p>The OpenCV-based SSD post-processing kernel is defined and implemented in this sample as follows:</p>
<div class="fragment"><div class="line"><span class="comment">// SSD Post-processing function - this is not a network but a kernel.</span></div><div class="line"><span class="comment">// The kernel body is declared separately, this is just an interface.</span></div><div class="line"><span class="comment">// This operation takes two Mats (detections and the source image),</span></div><div class="line"><span class="comment">// and returns a vector of ROI (filtered by a default threshold).</span></div><div class="line"><span class="comment">// Threshold (or a class to select) may become a parameter, but since</span></div><div class="line"><span class="comment">// this kernel is custom, it doesn&#39;t make a lot of sense.</span></div><div class="line"><a class="code" href="../../de/d7f/gkernel_8hpp.html#a75c975472536f4243e95150c51e8a3be">G_API_OP</a>(<a class="code" href="../../d4/d8e/namespacecv_1_1gapi_1_1onnx.html#adab664de6c184b183b729f62da973095">PostProc</a>, &lt;<a class="code" href="../../d3/d44/classcv_1_1GArray.html">cv::GArray&lt;cv::Rect&gt;</a>(<a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>, <a class="code" href="../../df/daa/classcv_1_1GMat.html">cv::GMat</a>)&gt;, <span class="stringliteral">&quot;custom.fd_postproc&quot;</span>) {</div><div class="line">    <span class="keyword">static</span> <a class="code" href="../../d7/dd5/structcv_1_1GArrayDesc.html">cv::GArrayDesc</a> outMeta(<span class="keyword">const</span> <a class="code" href="../../d0/d82/structcv_1_1GMatDesc.html">cv::GMatDesc</a> &amp;, <span class="keyword">const</span> <a class="code" href="../../d0/d82/structcv_1_1GMatDesc.html">cv::GMatDesc</a> &amp;) {</div><div class="line">        <span class="comment">// This function is required for G-API engine to figure out</span></div><div class="line">        <span class="comment">// what the output format is, given the input parameters.</span></div><div class="line">        <span class="comment">// Since the output is an array (with a specific type),</span></div><div class="line">        <span class="comment">// there&#39;s nothing to describe.</span></div><div class="line">        <span class="keywordflow">return</span> <a class="code" href="../../d3/d0c/group__gapi__meta__args.html#ga71b2cb69162686ddd6ada83c75d4b44e">cv::empty_array_desc</a>();</div><div class="line">    }</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">// OpenCV-based implementation of the above kernel.</span></div><div class="line"><a class="code" href="../../da/d73/gcpukernel_8hpp.html#aacef2b3c16c285adbd70a03ae8aedc46">GAPI_OCV_KERNEL</a>(OCVPostProc, <a class="code" href="../../d4/d8e/namespacecv_1_1gapi_1_1onnx.html#adab664de6c184b183b729f62da973095">PostProc</a>) {</div><div class="line">    <span class="keyword">static</span> <span class="keywordtype">void</span> run(<span class="keyword">const</span> <a class="code" href="../../d3/d63/classcv_1_1Mat.html">cv::Mat</a> &amp;in_ssd_result,</div><div class="line">                    <span class="keyword">const</span> <a class="code" href="../../d3/d63/classcv_1_1Mat.html">cv::Mat</a> &amp;in_frame,</div><div class="line">                    std::vector&lt;cv::Rect&gt; &amp;out_faces) {</div><div class="line">        <span class="keyword">const</span> <span class="keywordtype">int</span> MAX_PROPOSALS = 200;</div><div class="line">        <span class="keyword">const</span> <span class="keywordtype">int</span> OBJECT_SIZE   =   7;</div><div class="line">        <span class="keyword">const</span> <a class="code" href="../../d6/d50/classcv_1_1Size__.html">cv::Size</a> upscale = in_frame.<a class="code" href="../../d3/d63/classcv_1_1Mat.html#a146f8e8dda07d1365a575ab83d9828d1">size</a>();</div><div class="line">        <span class="keyword">const</span> <a class="code" href="../../d2/d44/classcv_1_1Rect__.html">cv::Rect</a> surface({0,0}, upscale);</div><div class="line"></div><div class="line">        out_faces.clear();</div><div class="line"></div><div class="line">        <span class="keyword">const</span> <span class="keywordtype">float</span> *data = in_ssd_result.<a class="code" href="../../d3/d63/classcv_1_1Mat.html#a13acd320291229615ef15f96ff1ff738">ptr</a>&lt;<span class="keywordtype">float</span>&gt;();</div><div class="line">        <span class="keywordflow">for</span> (<span class="keywordtype">int</span> i = 0; i &lt; MAX_PROPOSALS; i++) {</div><div class="line">            <span class="keyword">const</span> <span class="keywordtype">float</span> image_id   = data[i * OBJECT_SIZE + 0]; <span class="comment">// batch id</span></div><div class="line">            <span class="keyword">const</span> <span class="keywordtype">float</span> confidence = data[i * OBJECT_SIZE + 2];</div><div class="line">            <span class="keyword">const</span> <span class="keywordtype">float</span> rc_left    = data[i * OBJECT_SIZE + 3];</div><div class="line">            <span class="keyword">const</span> <span class="keywordtype">float</span> rc_top     = data[i * OBJECT_SIZE + 4];</div><div class="line">            <span class="keyword">const</span> <span class="keywordtype">float</span> rc_right   = data[i * OBJECT_SIZE + 5];</div><div class="line">            <span class="keyword">const</span> <span class="keywordtype">float</span> rc_bottom  = data[i * OBJECT_SIZE + 6];</div><div class="line"></div><div class="line">            <span class="keywordflow">if</span> (image_id &lt; 0.f) {  <span class="comment">// indicates end of detections</span></div><div class="line">                <span class="keywordflow">break</span>;</div><div class="line">            }</div><div class="line">            <span class="keywordflow">if</span> (confidence &lt; 0.5f) { <span class="comment">// a hard-coded snapshot</span></div><div class="line">                <span class="keywordflow">continue</span>;</div><div class="line">            }</div><div class="line"></div><div class="line">            <span class="comment">// Convert floating-point coordinates to the absolute image</span></div><div class="line">            <span class="comment">// frame coordinates; clip by the source image boundaries.</span></div><div class="line">            <a class="code" href="../../d2/d44/classcv_1_1Rect__.html">cv::Rect</a> rc;</div><div class="line">            rc.<a class="code" href="../../d2/d44/classcv_1_1Rect__.html#a2cadfdc3b4b7dbf8085622b27e044572">x</a>      = <span class="keyword">static_cast&lt;</span><span class="keywordtype">int</span><span class="keyword">&gt;</span>(rc_left   * upscale.<a class="code" href="../../d6/d50/classcv_1_1Size__.html#abfe0367b32c407ddccf5ddf92667c73d">width</a>);</div><div class="line">            rc.<a class="code" href="../../d2/d44/classcv_1_1Rect__.html#a6a4860e984df1752623b6ce2a8bde73a">y</a>      = <span class="keyword">static_cast&lt;</span><span class="keywordtype">int</span><span class="keyword">&gt;</span>(rc_top    * upscale.<a class="code" href="../../d6/d50/classcv_1_1Size__.html#a1d289dce6b5d8006a54f3ee0259fc545">height</a>);</div><div class="line">            rc.<a class="code" href="../../d2/d44/classcv_1_1Rect__.html#a6c16a3bce912faa4fe5be42d7f1b53fe">width</a>  = <span class="keyword">static_cast&lt;</span><span class="keywordtype">int</span><span class="keyword">&gt;</span>(rc_right  * upscale.<a class="code" href="../../d6/d50/classcv_1_1Size__.html#abfe0367b32c407ddccf5ddf92667c73d">width</a>)  - rc.<a class="code" href="../../d2/d44/classcv_1_1Rect__.html#a2cadfdc3b4b7dbf8085622b27e044572">x</a>;</div><div class="line">            rc.<a class="code" href="../../d2/d44/classcv_1_1Rect__.html#a6fed06513cedd76652389e38c7b1222e">height</a> = static_cast&lt;int&gt;(rc_bottom * upscale.<a class="code" href="../../d6/d50/classcv_1_1Size__.html#a1d289dce6b5d8006a54f3ee0259fc545">height</a>) - rc.<a class="code" href="../../d2/d44/classcv_1_1Rect__.html#a6a4860e984df1752623b6ce2a8bde73a">y</a>;</div><div class="line">            out_faces.push_back(rc &amp; surface);</div><div class="line">        }</div><div class="line">    }</div><div class="line">};</div></div><!-- fragment --></div></div><!-- contents -->
<!-- HTML footer for doxygen 1.8.6-->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated on Fri Apr 2 2021 11:36:34 for OpenCV by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="../../doxygen.png" alt="doxygen"/>
</a> 1.8.13
</small></address>
<script type="text/javascript">
//<![CDATA[
addTutorialsButtons();
//]]>
</script>
</body>
</html>
